package system

import (
	"context"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/os/gfile"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/sagoo-cloud/sagooiot/api/v1/system"
	"github.com/sagoo-cloud/sagooiot/internal/model"
	"github.com/sagoo-cloud/sagooiot/internal/service"
	"github.com/sagoo-cloud/sagooiot/utility/version"
)

var Application = cSystemApplication{}

type cSystemApplication struct{}

// GetApplicationList 获取列表
func (u *cSystemApplication) GetApplicationList(ctx context.Context, req *system.GetApplicationListReq) (res *system.GetApplicationListRes, err error) {
	var reqData = new(model.GetApplicationListInput)
	gconv.Scan(req, &reqData)
	total, currentPage, dataList, err := service.SystemApplication().GetApplicationList(ctx, reqData)
	res = new(system.GetApplicationListRes)
	gconv.Scan(dataList, &res.Data)
	res.PaginationRes.Total = total
	res.PaginationRes.CurrentPage = currentPage
	return
}

// GetApplicationById 获取指定ID数据
func (u *cSystemApplication) GetApplicationById(ctx context.Context, req *system.GetApplicationByIdReq) (res *system.GetApplicationByIdRes, err error) {
	data, err := service.SystemApplication().GetApplicationById(ctx, req.Id)
	res = new(system.GetApplicationByIdRes)
	gconv.Scan(data, &res)
	return
}

// AddApplication 添加数据
func (u *cSystemApplication) AddApplication(ctx context.Context, req *system.AddApplicationReq) (res *system.AddApplicationRes, err error) {
	userInfo := service.Context().GetLoginUser(ctx)
	if userInfo == nil {
		err = gerror.New("未登录或TOKEN失效,请重新登录")
		return
	}
	var data = model.ApplicationAddInput{}
	gconv.Scan(req, &data)
	// data.CreatedBy = userInfo.Id
	err = service.SystemApplication().AddApplication(ctx, data)
	return
}

// EditApplication 修改数据
func (u *cSystemApplication) EditApplication(ctx context.Context, req *system.EditApplicationReq) (res *system.EditApplicationRes, err error) {
	userInfo := service.Context().GetLoginUser(ctx)
	if userInfo == nil {
		err = gerror.New("未登录或TOKEN失效,请重新登录")
		return
	}
	var data = model.ApplicationEditInput{}
	gconv.Scan(req, &data)
	// data.UpdateBy = userInfo.Id //如果需要保存信息，把这个打开
	err = service.SystemApplication().EditApplication(ctx, data)
	return
}

// SetApplicationStatus 启动/禁用 应用管理
func (u *cSystemApplication) SetApplicationStatus(ctx context.Context, req *system.SetApplicationStatusReq) (res *system.SetApplicationStatusRes, err error) {
	return service.SystemApplication().SetApplicationStatus(ctx, req)
}

// DeleteApplication 删除数据
func (u *cSystemApplication) DeleteApplication(ctx context.Context, req *system.DeleteApplicationReq) (res *system.DeleteApplicationRes, err error) {
	if len(req.Ids) == 0 {
		err = gerror.New("ID参数错误")
		return
	}
	err = service.SystemApplication().DeleteApplication(ctx, req.Ids)
	return
}

// UploadApp 上传升级包,平滑重启应用
func (u *cSystemApplication) UploadApp(ctx context.Context, req *system.UploadReq) (res *system.UploadRes, err error) {
	if runtime.GOOS == "windows" {
		return nil, gerror.New(" windows dose not supported ")
	}
	path, _ := os.Getwd()
	dir := path + "/upload/app/" + time.Now().Format("2006-01-02") + "/"
	if !gfile.Exists(dir) {
		if err = gfile.Mkdir(dir); err != nil {
			return
		}
	}
	name, err := req.File.Save(dir, false)
	if err != nil {
		return
	}

	err = exec.Command("chmod", "+x", dir+name).Run()
	if err != nil {
		return
	}

	dirBak := "./upload/app_bak/"
	if !gfile.Exists(dirBak) {
		if err = gfile.Mkdir(dirBak); err != nil {
			return
		}
	}
	err = exec.Command("cp", os.Args[0], dirBak+os.Args[0]+"_"+time.Now().Format("2006_01_02_15_04_05")).Run()
	if err != nil {
		return
	}

	//cp是将源文件截断然后写入新内容。也就是说正在打开这个文件的进程可以立刻感知到修改。修改文件内容很可能导致程序逻辑错误甚至崩溃。而mv则是标记”删除“老文件，然后放一个新的同名文件过去。也就是说老文件和新文件其实是两个不同文件（inode不同），只是名字一样而已。正在打开老文件的进程不会受到影响。如果进程使用了mmap打开某文件（比如载入so），如果目标文件被使用cp覆盖并且长度变小。那么读取差额部分的地址时（在新文件中其实已经不存在了），会导致SIGBUS信号。使进程崩溃。
	// 至于可执行文件本身。倒是不怕cp导致崩溃。。因为cp时会报”text file busy"。压根cp不了。这时候也应该使用mv类操作。替换完成后重启进程。执行的就是新的可执行文件了。
	err = exec.Command("mv", dir+name, "./").Run()
	if err != nil {
		return nil, gerror.New(err.Error())
	}
	_, err = service.SystemApplication().RestartApplication(ctx, &system.RestartApplicationListReq{})
	return

}

// SystemInfo app 版本信息
func (u *cSystemApplication) SystemInfo(ctx context.Context, req *system.VersionInfoReq) (res *system.VersionInfoRes, err error) {

	res = &system.VersionInfoRes{}
	res.Version = version.BuildVersion
	res.BuildTime = version.BuildTime
	res.VersionDesc = version.VersionDesc
	return
}
